#include <boost/bind.hpp>
#include <boost/function.hpp>

#include <iostream>
#include <set>

#include "building_type.hpp"
#include "button.hpp"
#include "city.hpp"
#include "city_dialog.hpp"
#include "city_history_dialog.hpp"
#include "foreach.hpp"
#include "formatter.hpp"
#include "government.hpp"
#include "grid_widget.hpp"
#include "image_widget.hpp"
#include "job.hpp"
#include "label.hpp"
#include "occupation.hpp"
#include "project.hpp"
#include "raster.hpp"
#include "string_utils.hpp"
#include "translate.hpp"
#include "unit.hpp"
#include "unit_type.hpp"

namespace game_dialogs {

city_dialog::city_dialog(world& w, city_ptr c) : dialog(0, 0, graphics::screen_width(), graphics::screen_height()), world_(w), city_(c)
{
	init();
}

void city_dialog::init()
{
	clear();

	using namespace gui;

	const SDL_Color color = { 0xFF, 0xFF, 0xFF };

	set_padding(30);

	std::set<const_item_type_ptr> items;
	city_->market().goods_traded(items);

	const int treasury = city_->owner()->treasury();

	for(city::items_map::const_iterator i = city_->items().begin(); i != city_->items().end(); ++i) {
		items.insert(i->first);
	}

	label_factory lb(color, 20);

	grid_ptr grid(new gui::grid(11));
	grid->set_hpad(10);
	grid->add_col(lb.create("Item")).
	      add_col(lb.create("Volume")).
		  add_col(lb.create("Revenue")).
	      add_col(lb.create("Owned")).
	      add_col(lb.create("Capacity")).
	      add_col(lb.create("Buy")).
	      add_col(lb.create("Price")).
	      add_col(lb.create("Sell")).
	      add_col(lb.create("Price")).
	      add_col(lb.create("Imports")).
	      add_col(lb.create("Exports"));

	for(std::set<const_item_type_ptr>::const_iterator i = items.begin(); i != items.end(); ++i) {
		const int quantity = city_->market().last_quantity((*i)->id());
		const int taxes = city_->market().last_taxes((*i)->id());
		grid->add_col(lb.create((*i)->id())).
		      add_col(lb.create(formatter() << quantity)).
		      add_col(lb.create(formatter() << taxes));
		city::items_map::const_iterator nowned = city_->items().find(*i);
		const int owned = nowned == city_->items().end() ? 0 : nowned->second;
		const int capacity = city_->storage_capacity(*i);
		grid->add_col(lb.create(formatter() << owned))
		     .add_col(lb.create(formatter() << capacity));

		const int buy_price = city_->market().can_buy_at(*i);
		const int sell_price = city_->market().can_sell_at(*i);

		if(city_->market().buy_item(*i, buy_price, false) == false) {
			grid->add_col(lb.create("")).add_col(lb.create("N/A"));
		} else {
			if(buy_price <= treasury && city_->market().can_buy(*i) && owned < capacity) {
				boost::function<void ()> callback(boost::bind(&city_dialog::buy, this, *i));
				grid->add_col(widget_ptr(new button(widget_ptr(new label("Buy", color)), callback)));
			} else {
				grid->add_col(lb.create(""));
			}

			grid->add_col(lb.create(formatter() << buy_price));
		}

		if(city_->market().sell_item(*i, sell_price, false) == false) {
			grid->add_col(lb.create("")).add_col(lb.create("N/A"));
		} else {
			if(city_->market().can_sell(*i) && city_->items().count(*i) && city_->items().find(*i)->second) {
				boost::function<void ()> callback(boost::bind(&city_dialog::sell, this, *i));
				grid->add_col(widget_ptr(new button(widget_ptr(new label("Sell", color)), callback)));
			} else {
				grid->add_col(lb.create(""));
			}

			grid->add_col(lb.create(formatter() << sell_price));
		}

		int nimports = 0, nexports = 0;
		foreach(const merchant_trade& trade, city_->imports()) {
			if(trade.item == *i) {
				nimports++;
			}
		}

		foreach(const merchant_trade& trade, city_->exports()) {
			if(trade.item == *i) {
				nexports++;
			}
		}

		grid->add_col(lb.create(formatter() << nimports))
		     .add_col(lb.create(formatter() << nexports));
	}

	add_widget(grid, 20, 20);

	add_widget(lb.create(formatter() << "Treasury: " << treasury));

	grid.reset(new gui::grid(5));
	grid->set_hpad(10);
	grid->add_col(lb.create("Occupation")).
	      add_col(lb.create("Employees")).
		  add_col(lb.create("Wages")).
		  add_col(lb.create("Hire")).
		  add_col(lb.create("Cost"));

	foreach(job_ptr j, city_->jobs()) {
		boost::function<void ()> hire_cb(boost::bind(&city_dialog::hire, this, j));
		button_ptr hire_button(new button(widget_ptr(new label("Hire", color)), hire_cb));
		const int next_wages = city_->add_worker_cost(world_, j);
		grid->add_col(lb.create(j->type()->id())).
		      add_col(lb.create(formatter() << j->positions_taken())).
			  add_col(lb.create(formatter() << (j->pay() * j->positions_taken())));

		if(next_wages > 0) {
			grid->add_col(hire_button).
			      add_col(lb.create(formatter() << (next_wages * (j->positions_taken() + 1))));
		} else {
			grid->add_col(lb.create("")).add_col(lb.create(""));
		}
	}

	add_widget(grid);

	grid.reset(new gui::grid(3));
	grid->set_hpad(10);
	grid->add_col(lb.create("Project")).
	      add_col(lb.create("Complete")).
	      add_col(lb.create("Required"));
	foreach(project_ptr p, city_->projects()) {
		grid->add_col(lb.create(p->description())).
		      add_col(lb.create(formatter() << p->labor_complete())).
		      add_col(lb.create(formatter() << p->labor_required()));
	}

	add_widget(grid);

	grid.reset(new gui::grid(2));
	grid->set_hpad(10);
	grid->add_col(lb.create("Building")).
	      add_col(lb.create("Number"));
	for(city::buildings_map::const_iterator i = city_->buildings().begin(); i != city_->buildings().end(); ++i) {
		grid->add_col(lb.create(i->first->id()))
		     .add_col(lb.create(formatter() << i->second));
	}

	add_widget(grid);

	grid.reset(new gui::grid(2));
	grid->set_hpad(10);
	grid->add_col(lb.create("Occupation"))
	     .add_col(lb.create("Citizens"));
	std::map<const occupation*, int> occupations;
	foreach(const_citizen_ptr c, city_->tile().citizens()) {
		if(c->get_occupation()) {
			occupations[c->get_occupation()]++;
		}
	}

	for(std::map<const occupation*, int>::const_iterator i = occupations.begin(); i != occupations.end(); ++i) {
		grid->add_col(lb.create(i->first->description()))
		     .add_col(lb.create(formatter() << i->second));
	}

	add_widget(grid);

	grid.reset(new gui::grid(2));
	grid->set_hpad(10);
	grid->add_col(lb.create("Unit"))
	     .add_col(lb.create("Progress"));

	foreach(const city::unit_in_production& u, city_->units_in_production()) {
		grid->add_col(lb.create(u.unit->type()->description()))
		     .add_col(lb.create(formatter() << u.training_done << "/" << u.training_required));
	}

	add_widget(grid);

	widget_ptr history_label(new label("History", color));
	button_ptr history_button(new button(history_label, boost::bind(&city_dialog::show_history_dialog, this)));
	add_widget(history_button);

	widget_ptr close_label(new label("Close", color));
	boost::function<void ()> close_callback(boost::bind(&dialog::close, this));
	button_ptr close_button(new button(close_label,close_callback));
	add_widget(close_button, graphics::screen_width()-100, graphics::screen_height()-100);
}

void city_dialog::buy(const_item_type_ptr i)
{
	const int price = city_->market().can_buy_at(i);
	if(city_->market().buy_item(i, price)) {
		city_->owner()->get_money(-price);
		city_->get_items(i, 1);
		city_->market().prepare_market();
		init();
	}
}

void city_dialog::sell(const_item_type_ptr i)
{
	const int price = city_->market().can_sell_at(i);
	if(city_->market().sell_item(i, price)) {
		city_->owner()->get_money(price);
		city_->get_items(i, -1);
		city_->market().prepare_market();
		init();
	}
}

void city_dialog::hire(job_ptr j)
{
	city_->hire_worker(world_, j);
	init();
}

void city_dialog::show_history_dialog()
{
	game_dialogs::city_history_dialog d(world_, city_);
	d.show_modal();
}

}
